package com.yhos.platform.oopston.core.rest;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.yhos.platform.oopston.core.aop.ParamsBean;
import com.yhos.platform.oopston.core.aop.ParamsThreadLocalManager;
import com.yhos.platform.oopston.core.base.PageResponse;
import com.yhos.platform.oopston.core.base.Response;
import com.yhos.platform.oopston.core.constant.OperatorConstant;
import com.yhos.platform.oopston.core.utils.JacksonUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.UUID;

public class RestCall {

    private static final Logger LOGGER = LoggerFactory.getLogger(RestCall.class);

    private ObjectMapper objectMapper = JacksonUtils.getInstance();

    private ParamsThreadLocalManager paramsThreadLocalManager = ParamsThreadLocalManager.getInstance();

    private RestTemplate restTemplate;

    public <T> PageResponse<List<T>> requestForPage(String restUrl, String restApi, Object requestModel, ParameterizedTypeReference<PageResponse<List<T>>> resultClass) {
        if (StringUtils.isBlank(restUrl) || StringUtils.isBlank(restApi)) {
            return null;
        }

        ParamsBean params = paramsThreadLocalManager.getParams();
        Map<String, Object> paramValueMap = params != null ? params.getAllRequestParamsMap() : null;

        //Header中传递User Token
        HttpHeaders headers = new HttpHeaders();
        MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
        headers.setContentType(type);
        headers.add("Accept", MediaType.APPLICATION_JSON.toString());
        String token = null;
        if (paramValueMap  != null && paramValueMap.containsKey(OperatorConstant.USER_TOKEN)) {
            token = (String) paramValueMap.get(OperatorConstant.USER_TOKEN);
        }

        if (StringUtils.isNotBlank(token)) {
            headers.add(OperatorConstant.USER_TOKEN, token);
        }

        //Header中传递Request UUID
        String fromSourceId;
        if (paramValueMap  != null && paramValueMap.containsKey(OperatorConstant.FROM_SOURCE_ID)) {
            fromSourceId = (String) paramValueMap.get(OperatorConstant.FROM_SOURCE_ID);
        } else {
            fromSourceId = UUID.randomUUID().toString();
        }
        if (StringUtils.isNotBlank(fromSourceId)) {
            headers.add(OperatorConstant.FROM_SOURCE_ID, fromSourceId);
        }

        //Header中传递来源
        String fromSource = null;
        InputStreamReader configStream = null;
        try {
            configStream = new InputStreamReader(getClass().getClassLoader().getResourceAsStream("META-INF/app.properties"), "UTF-8");
            Properties configProperties = new Properties();
            configProperties.load(configStream);
            fromSource = configProperties.getProperty("app.id");
        } catch (IOException e) {
            LOGGER.info("reloadRuntimeProperties error.", e);
        } finally {
            if (configStream != null) {
                try {
                    configStream.close();
                } catch (IOException e) {
                    throw new RuntimeException("close runtimeProperties error.", e);
                }
            }
        }
        if (StringUtils.isNotBlank(fromSource)) {
            headers.add(OperatorConstant.FROM_SOURCE, fromSource);
        }

        HttpEntity<Object> formEntity = new HttpEntity<>(requestModel, headers);

        String postUrl = restUrl + restApi;
        ResponseEntity<PageResponse<List<T>>> resultContent = null;
        long startTime = System.currentTimeMillis();

        String requestHeader = "\n request header : " + getObjectContent(headers);
        String requestContent = "\n , request content : " + getObjectContent(requestModel);
        try {
            resultContent = restTemplate.exchange(postUrl, HttpMethod.POST, formEntity, resultClass);
        } catch (Exception ex) {
            LOGGER.error("restClient request url : " + postUrl + requestHeader + requestContent + "\n , error message : " + ex.getMessage(), ex);
            resultContent = null;
        } finally {
            long endTime = System.currentTimeMillis();
            String responseContent = "\n , response result : " + getObjectContent(resultContent);
            LOGGER.info("restClient request url : " + postUrl + " , use time : " + (endTime - startTime) + requestHeader + requestContent + responseContent);
        }

        return resultContent.getBody();
    }

    public <T> Response<T> request(String restUrl, String restApi, Object requestModel, ParameterizedTypeReference<Response<T>> resultClass) {
        if (StringUtils.isBlank(restUrl) || StringUtils.isBlank(restApi)) {
            return null;
        }

        ParamsBean params = paramsThreadLocalManager.getParams();
        Map<String, Object> paramValueMap = params != null ? params.getAllRequestParamsMap() : null;

        //Header中传递User Token
        HttpHeaders headers = new HttpHeaders();
        MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
        headers.setContentType(type);
        headers.add("Accept", MediaType.APPLICATION_JSON.toString());
        String token = null;
        if (paramValueMap  != null && paramValueMap.containsKey(OperatorConstant.USER_TOKEN)) {
            token = (String) paramValueMap.get(OperatorConstant.USER_TOKEN);
        }

        if (StringUtils.isNotBlank(token)) {
            headers.add(OperatorConstant.USER_TOKEN, token);
        }

        //Header中传递Request UUID
        String fromSourceId;
        if (paramValueMap  != null && paramValueMap.containsKey(OperatorConstant.FROM_SOURCE_ID)) {
            fromSourceId = (String) paramValueMap.get(OperatorConstant.FROM_SOURCE_ID);
        } else {
            fromSourceId = UUID.randomUUID().toString();
        }
        if (StringUtils.isNotBlank(fromSourceId)) {
            headers.add(OperatorConstant.FROM_SOURCE_ID, fromSourceId);
        }

        //Header中传递来源
        String fromSource = null;
        InputStreamReader configStream = null;
        try {
            configStream = new InputStreamReader(getClass().getClassLoader().getResourceAsStream("META-INF/app.properties"), "UTF-8");
            Properties configProperties = new Properties();
            configProperties.load(configStream);
            fromSource = configProperties.getProperty("app.id");
        } catch (IOException e) {
            LOGGER.info("reloadRuntimeProperties error.", e);
        } finally {
            if (configStream != null) {
                try {
                    configStream.close();
                } catch (IOException e) {
                    throw new RuntimeException("close runtimeProperties error.", e);
                }
            }
        }
        if (StringUtils.isNotBlank(fromSource)) {
            headers.add(OperatorConstant.FROM_SOURCE, fromSource);
        }

        HttpEntity<Object> formEntity = new HttpEntity<>(requestModel, headers);

        String postUrl = restUrl + restApi;
        ResponseEntity<Response<T>> resultContent = null;
        long startTime = System.currentTimeMillis();

        String requestHeader = "\n request header : " + getObjectContent(headers);
        String requestContent = "\n , request content : " + getObjectContent(requestModel);
        try {
            resultContent = restTemplate.exchange(postUrl, HttpMethod.POST, formEntity, resultClass);
        } catch (Exception ex) {
            LOGGER.error("restClient request url : " + postUrl + requestHeader + requestContent + "\n , error message : " + ex.getMessage(), ex);
            resultContent = null;
        } finally {
            long endTime = System.currentTimeMillis();
            String responseContent = "\n , response result : " + getObjectContent(resultContent);
            LOGGER.info("restClient request url : " + postUrl + " , use time : " + (endTime - startTime) + requestHeader + requestContent + responseContent);
        }

        return resultContent.getBody();
    }

    private String getObjectContent(Object object) {
        if (object == null) {
            return "";
        }

        try {
            return objectMapper.writeValueAsString(object);
        } catch (IOException e) {
            LOGGER.error("getObjectContent writeValueAsString error." + e.getMessage(), e);
        }
        return "";
    }

    public void setRestTemplate(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }
}
